/**
 * @file wa_roller.c
 *
 */

 /*********************
 *      INCLUDES
 *********************/
#include "wa_conf.h"
#if USE_WA_ROLLER != 0
#include "wa_roller.h"
#include "wa_pid.h"
#include "wa_input.h"

 /*********************
 *      DEFINES
 *********************/

 /**********************
 *      TYPEDEFS
 **********************/

 /**********************
 *  STATIC PROTOTYPES
 **********************/
static wa_obj_callback ancestor_callback;
static void wa_roller_callback(wa_sign* sign);
#if USE_WA_ANIM != 0
static void wa_roller_anim_exec_cb(void* obj, int32_t value);
static void wa_roller_anim_finish_cb(wa_anim* anim);
static void wa_roller_anim_start_cb(wa_anim* anim);
#endif
static int32_t wa_roller_select_to_pos(wa_obj* roller);
static int32_t wa_roller_find_min_voption(wa_obj* roller);
static int32_t wa_roller_find_max_voption(wa_obj* roller);
/**********************
 *  STATIC VARIABLES
 **********************/

 /**********************
 *      MACROS
 **********************/

 /**********************
 *   GLOBAL FUNCTIONS
 **********************/
 /**
 * roller module init
 */
void wa_roller_module_init(void)
{
}

/**
* create a roller
* @param par
* @return roller obj
*/
wa_obj* wa_roller_create(wa_obj* par)
{
	wa_obj* new_roller;
	wa_roller_ext* roller_ext;

	new_roller = wa_obj_create(par, NULL);
	if (new_roller == NULL)
	{
		return new_roller;
	}

	if (ancestor_callback == NULL)
	{
		ancestor_callback = wa_obj_get_callback(new_roller);
	}
	roller_ext = wa_obj_malloc_ext_attr(new_roller, sizeof(wa_roller_ext));
	if (roller_ext == NULL)
	{
		return NULL;
	}
#if USE_WA_ANIM != 0
	roller_ext->anim = wa_anim_create();
#endif
	roller_ext->type = WA_ROLLER_TYPE_HOR;
	roller_ext->font = NULL;
	roller_ext->option_text = NULL;
	roller_ext->option_cnt = 0;
	roller_ext->select = 0;
	roller_ext->space = 0;
	roller_ext->anim_enable = wa_nok;
	roller_ext->switch_time_length = 500;

	roller_ext->fore_color = WA_COLOR_FOREGROUND;
	roller_ext->back_color = WA_COLOR_BACKGROUND;

	roller_ext->pos = 0;

	wa_obj_set_callback(new_roller, wa_roller_callback);

	return new_roller;
}

/**
* roller add option
* @param roller
* @param text
*/
void wa_roller_add_option(wa_obj* roller, char* text)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);

	if (ext->option_text == NULL)
	{
		ext->option_text = wa_malloc(sizeof(char*));
		ext->option_text[0] = wa_malloc(strlen((const char*)text) + 1);
		strcpy((char*)(ext->option_text[0]), (char*)text);
		ext->option_cnt = 1;
	}
	else
	{
		char** p;
		ext->option_cnt++;
		p = wa_malloc(sizeof(char*) * ext->option_cnt);
		memcpy(p, ext->option_text, sizeof(char*) * (ext->option_cnt - 1));
		wa_free(ext->option_text);
		ext->option_text = p;
		ext->option_text[ext->option_cnt - 1] = wa_malloc(strlen((const char*)text) + 1);
		strcpy((char*)(ext->option_text[ext->option_cnt - 1]), (char*)text);
	}
	wa_obj_invalidate(roller);
}

/**
* roller clear option
* @param roller
*/
void wa_roller_clear_option(wa_obj* roller)
{
	uint32_t i;
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	if (ext->option_text == NULL)
	{
		return;
	}
	for (i = 0; i < ext->option_cnt; i++)
	{
		wa_free(ext->option_text[i]);
	}
	wa_free(ext->option_text);
	wa_obj_invalidate(roller);
}

/**
* roller set select
* @param roller
* @param select
*/
void wa_roller_set_select(wa_obj* roller, uint32_t select)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	if (select <= 0)
	{
		select = 0;
	}
	if (select > (ext->option_cnt - 1))
	{
		select = (ext->option_cnt - 1);
	}
	if (select == ext->select)
	{
		return;
	}
#if USE_WA_ANIM != 0
	if (ext->anim_enable == wa_ok)
	{
		int32_t last_pos;
		int32_t pos;
		last_pos = wa_roller_select_to_pos(roller);
		ext->select = select;
		wa_wm_notify_parent(roller, WA_SIGNAL_NOTIFY_SEL_CHANGED);
		pos = wa_roller_select_to_pos(roller);

		wa_anim_set_var(ext->anim, roller);
		wa_anim_set_exec_cb(ext->anim, wa_roller_anim_exec_cb);
		wa_anim_set_start_cb(ext->anim, wa_roller_anim_start_cb);
		wa_anim_set_finish_cb(ext->anim, wa_roller_anim_finish_cb);
		wa_anim_set_path_cb(ext->anim, wa_anim_path_ease_in_out);
		wa_anim_set_time_long(ext->anim, ext->switch_time_length);
		wa_anim_set_early_apply(ext->anim, wa_nok);
		wa_anim_set_values(ext->anim, last_pos, pos);
		wa_anim_start(ext->anim);
	}
	else
#endif
	{
		ext->select = select;
		wa_wm_notify_parent(roller, WA_SIGNAL_NOTIFY_SEL_CHANGED);
		ext->pos = wa_roller_select_to_pos(roller);
		wa_obj_invalidate(roller);
	}
}

/**
* roller set type
* @param roller
* @param type
*/
void wa_roller_set_type(wa_obj* roller, wa_roller_type type)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->type = type;
	wa_obj_invalidate(roller);
}

/**
* roller set font
* @param roller
* @param font
*/
void wa_roller_set_font(wa_obj* roller, wa_font* font)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->font = font;
	wa_obj_invalidate(roller);
}

/**
* roller set space
* @param roller
* @param space
*/
void wa_roller_set_space(wa_obj* roller, uint32_t space)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->space = space;
	wa_obj_invalidate(roller);
}

/**
* roller set anim enable
* @param roller
* @param anim enable
*/
void wa_roller_set_anim_enable(wa_obj* roller, wa_result anim_enable)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->anim_enable = anim_enable;
}

/**
* roller set swtich time length
* @param roller
* @param time length
*/
void wa_roller_set_switch_time_length(wa_obj* roller, uint32_t length)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->switch_time_length = length;
}

/**
* roller set fore color
* @param roller
* @param fore color
*/
void wa_roller_set_fore_color(wa_obj* roller, wa_gui_color fore_color)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->fore_color = fore_color;
	wa_obj_invalidate(roller);
}

/**
* roller set back color
* @param roller
* @param back color
*/
void wa_roller_set_back_color(wa_obj* roller, wa_gui_color back_color)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->back_color = back_color;
	wa_obj_invalidate(roller);
}

/**
* roller get select
* @param roller
* @return select
*/
uint32_t wa_roller_get_select(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->select;
}

/**
* roller get type
* @param roller
* @return type
*/
wa_roller_type wa_roller_get_type(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->type;
}

/**
* roller get font
* @param roller
* @return font
*/
wa_font* wa_roller_get_font(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->font;
}

/**
* roller get space
* @param roller
* @return space
*/
uint32_t wa_roller_get_space(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->space;
}

/**
* roller get anim enable
* @param roller
* @return anim enable
*/
wa_result wa_roller_get_anim_enable(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->anim_enable;
}

/**
* roller get switch time length
* @param roller
* @return switch time length
*/
uint32_t wa_roller_get_switch_time_length(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->switch_time_length;
}

/**
* roller get fore color
* @param roller
* @return fore color
*/
wa_gui_color wa_roller_get_fore_color(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->fore_color;
}

/**
* roller get back color
* @param roller
* @return back color
*/
wa_gui_color wa_roller_get_back_color(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	return ext->back_color;
}

/**
* roller init select pos
* @param roller
*/
void wa_roller_init_select_pos(wa_obj* roller)
{
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->pos = wa_roller_select_to_pos(roller);
	wa_obj_invalidate(roller);
}

/**********************
*   STATIC FUNCTIONS
**********************/
#if USE_WA_ANIM != 0
/**
* roller anim exec cb
* @param obj
* @param value
*/
static void wa_roller_anim_exec_cb(void* obj, int32_t value)
{
	wa_obj* roller = (wa_obj*)obj;
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	ext->pos = value;
	wa_obj_invalidate(roller);
}

/**
* roller anim finish cb
* @param anim
*/
static void wa_roller_anim_finish_cb(wa_anim* anim)
{
	wa_obj* roller = (wa_obj*)(anim->var);
	wa_roller_ext* ext = wa_obj_get_ext_attr(roller);
	if (ext->pid == wa_ok)
	{
		wa_pid_enable(wa_ok);
	}
	if (ext->input == wa_ok)
	{
		wa_input_enable(wa_ok);
	}
}

/**
* roller anim start cb
* @param anim
*/
static void wa_roller_anim_start_cb(wa_anim* anim)
{
	wa_obj* roller = (wa_obj*)(anim->var);
	wa_roller_ext* ext = wa_obj_get_ext_attr(roller);
	if (wa_pid_is_enable() == wa_ok)
	{
		ext->pid = wa_ok;
		wa_pid_enable(wa_nok);
	}
	else
	{
		ext->pid = wa_nok;
	}

	if (wa_input_is_enable() == wa_ok)
	{
		ext->input = wa_ok;
		wa_input_enable(wa_nok);
	}
	else
	{
		ext->input = wa_nok;
	}
}
#endif

/**
* roller find min voption
* @param roller
* @return min voption
*/
static int32_t wa_roller_find_min_voption(wa_obj* roller)
{
	int w, h;
	uint32_t i;
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	if (ext->type == WA_ROLLER_TYPE_HOR)
	{
		w = 0;
		for (i = 0; i < ext->option_cnt; i++)
		{
			int sw, sh;
			char* p = ext->option_text[i];
			wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
			w += sw;

			if (ext->pos < w)
			{
				return i;
			}
			w += ext->space;
		}
		return -1;
	}
	else if (ext->type == WA_ROLLER_TYPE_VER)
	{
		h = 0;
		for (i = 0; i < ext->option_cnt; i++)
		{
			int sw, sh;
			char* p = ext->option_text[i];
			wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
			h += sh;
			if (ext->pos < h)
			{
				return i;
			}
			h += ext->space;
		}
		return -1;
	}
	return -1;
}

/**
* roller find max voption
* @param roller
* @return max voption
*/
static int32_t wa_roller_find_max_voption(wa_obj* roller)
{
	int w, h;
	uint32_t i;
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	if (ext->type == WA_ROLLER_TYPE_HOR)
	{
		int width = wa_obj_get_width(roller);
		w = 0;
		for (i = 0; i < ext->option_cnt; i++)
		{
			int sw, sh;
			char* p = ext->option_text[i];
			wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
			w += sw;

			if ((ext->pos + width) <= w)
			{
				return i;
			}
			w += ext->space;
		}
		return (ext->option_cnt - 1);
	}
	else if (ext->type == WA_ROLLER_TYPE_VER)
	{
		int height = wa_obj_get_height((wa_obj*)roller);
		h = 0;
		for (i = 0; i < ext->option_cnt; i++)
		{
			int sw, sh;
			char* p = ext->option_text[i];
			wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
			h += sh;

			if ((ext->pos + height) <= h)
			{
				return i;
			}
			h += ext->space;
		}
		return (ext->option_cnt - 1);
	}
	return -1;
}

/**
* roller select to pos
* @param roller
* @return pos
*/
static int32_t wa_roller_select_to_pos(wa_obj* roller)
{
	int32_t w, h;
	uint32_t i;
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(roller);
	if (ext->type == WA_ROLLER_TYPE_HOR)
	{
		int sw, sh;
		char* p;
		w = 0;
		for (i = 0; i < ext->select; i++)
		{
			p = ext->option_text[i];
			wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
			w += sw;
			w += ext->space;
		}
		p = ext->option_text[ext->select];
		wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
		return w - (wa_obj_get_width((wa_obj*)roller) - sw) / 2;
	}
	else if (ext->type == WA_ROLLER_TYPE_VER)
	{
		int sw, sh;
		char* p;
		h = 0;
		for (i = 0; i < ext->select; i++)
		{
			p = ext->option_text[i];
			wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
			h += sh;
			h += ext->space;
		}
		p = ext->option_text[ext->select];
		wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
		return h - (wa_obj_get_height((wa_obj*)roller) - sh) / 2;
	}
	return 0;
}

/**
* roller redraw functions
* @param obj
*/
static void _Paint(wa_obj* obj)
{
	uint32_t min, max;
	uint32_t i;
	int32_t x, y;
	uint32_t next_len;
	int32_t start_pos;
	int32_t pw, ph;
	wa_area str_area;

	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(obj);

	pw = wa_obj_get_width(obj);
	ph = wa_obj_get_height(obj);

	wa_mask_fillrect(obj->rect.x1, obj->rect.y1,
		pw,
		ph,
		&(obj->redraw_rect),
		ext->back_color);

	if (ext->option_cnt == 0)
	{
		return;
	}
	if (ext->option_text == NULL)
	{
		return;
	}
	if (ext->font == NULL)
	{
		return;
	}

	min = wa_roller_find_min_voption(obj);
	max = wa_roller_find_max_voption(obj);

	next_len = 0;
	for (i = 0; i < min; i++)
	{
		int sw, sh;
		char* p = ext->option_text[i];
		wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
		if (ext->type == WA_ROLLER_TYPE_HOR)
		{
			next_len += sw;
			next_len += ext->space;
		}
		else if (ext->type == WA_ROLLER_TYPE_VER)
		{
			next_len += sh;
			next_len += ext->space;
		}
	}
	start_pos = next_len - ext->pos;
	for (i = min; i <= max; i++)
	{
		int sw, sh;
		char* p = ext->option_text[i];
		wa_font_get_str_size(&sw, &sh, (uint8_t*)p, ext->font, 0, 0);
		if (ext->type == WA_ROLLER_TYPE_HOR)
		{
			x = start_pos;
			y = (ph - sh) / 2;

			str_area.x1 = obj->rect.x1 + x;
			str_area.y1 = obj->rect.y1 + y;
			str_area.x2 = str_area.x1 + sw - 1;
			str_area.y2 = str_area.y1 + sh - 1;
			wa_mask_dispstr_1(&str_area, (uint8_t*)p, ext->font, 0, 0, &(obj->redraw_rect), ext->fore_color);

			start_pos += sw;
			start_pos += ext->space;
		}
		else if (ext->type == WA_ROLLER_TYPE_VER)
		{
			x = (pw - sw) / 2;
			y = start_pos;

			str_area.x1 = obj->rect.x1 + x;
			str_area.y1 = obj->rect.y1 + y;
			str_area.x2 = str_area.x1 + sw - 1;
			str_area.y2 = str_area.y1 + sh - 1;
			wa_mask_dispstr_1(&str_area, (uint8_t*)p, ext->font, 0, 0, &(obj->redraw_rect), ext->fore_color);

			start_pos += sh;
			start_pos += ext->space;
		}
	}
}

/**
 * roller obj delete function
 * @param obj
 */
static void _Delete(wa_obj* obj)
{
#if USE_WA_ANIM != 0
	wa_roller_ext* ext;
	ext = wa_obj_get_ext_attr(obj);
	if (ext->anim != NULL)
	{
		wa_free(ext->anim);
	}
#endif
	wa_roller_clear_option(obj);
}

/**
 * roller obj callback function
 * @param sign
 */
static void wa_roller_callback(wa_sign* sign)
{
	int sign_id;
	wa_obj* obj;

	ancestor_callback(sign);
	sign_id = sign->sign_id;
	obj = sign->obj_dst;

	switch (sign_id)
	{
		case WA_SIGNAL_PAINT:
		{
			_Paint(obj);
			break;
		}
		case WA_SIGNAL_DELETE:
		{
			_Delete(obj);
			break;
		}
		default:
		{
			break;
		}
	}
}

#endif
